﻿using System.Windows;
using System.Collections.Generic;
using AzukiMap.Tools.Logger;
using System;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;

namespace AzukiMap.Layer
{
    public class LayerManager
    {
        ObservableCollection<ILayer> _layers = new ObservableCollection<ILayer>();
        Canvas _layerContainer;

        public int LayerCount
        {
            get { return _layers.Count; }
        }

        public ObservableCollection<ILayer> Layers
        {
            get { return _layers; }
        } 

        public LayerManager(Canvas layerContainer)
        {
            _layerContainer = layerContainer;
        }

        public void DrawAll()
        {
            foreach (ILayer layer in _layers)
            {
                layer.Draw();
            }
        }
            
        public void RemoveAllDrawings()
        {
            foreach (ILayer layer in _layers)
            {
                refresh(layer);
            }
        }

        public void refresh(ILayer layer)
        {
            layer.Clear();
        }

        public void Add(ILayer layer)
        {
            AddTop(layer);
        }

        public void AddTop(ILayer layer)
        {
            Add(layer, 0);
        }

        public void AddButtom(ILayer layer)
        {
            Add(layer, _layers.Count);
        }

        public void Add(ILayer layer, int position)
        {
            if (hasSameName(layer))
            {
                Logger.Error("Cannot add a layer has a existing layer name.");
                return;
            }
            if (position < 0)
            {
                position = 0;
            }
            if (position >= _layers.Count)
            {
                _layers.Add(layer);
                _layerContainer.Children.Add((UIElement)layer.MapCanvas); 
            }
            else
            {
                _layers.Insert(0, layer);
                _layerContainer.Children.Insert(0, (UIElement)layer.MapCanvas);
            }
            layer.Draw();

            SetZIndex();
        }


        public void Remove(ILayer layer)
        {
            if ( CheckLayerBelongs(layer)) {
                _layers.Remove(layer);
                SetZIndex();
            }
        }

        public int MoveUp(ILayer layer)
        {
            if ( ! CheckLayerBelongs(layer)) {
                return -1;
            }
            int currentOrder = _layers.IndexOf(layer);
            int listCount = _layers.Count;
            if (currentOrder == listCount - 1)
            {
                Logger.Warn("Layer '" + layer.Name + "' cannot move up.");
                return currentOrder;
            }
            int newOrder = currentOrder + 1;
            _layers.Remove(layer);
            _layers.Insert(newOrder, layer);

            SetZIndex();

            return newOrder;
        }

        public int MoveDown(ILayer layer)
        {
            if ( ! CheckLayerBelongs(layer)) {
                return -1;
            }

            int currentOrder = _layers.IndexOf(layer);
            if (currentOrder == 0)
            {
                Logger.Warn("Layer '" + layer.Name + "' cannot move down.");
                return currentOrder;
            }
            int newOrder = currentOrder - 1;
            _layers.Remove(layer);
            _layers.Insert(newOrder, layer);

            SetZIndex();

            return newOrder;
        }

        public ILayer GetLayer(string layerName)
        {
            foreach (ILayer layer in _layers)
            {
                if (layer.Name == layerName)
                {
                    return layer;
                }
            }
            throw new ArgumentException();
        }

        public void Show(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                layer.Show();
                //ShowLayer(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void Hide(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                layer.Hide();
                //HideLayer(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void Remove(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                Remove(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void MoveUp(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                MoveUp(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void MoveDown(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                MoveDown(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public XElement ToXml() {
            XElement layersXml = new XElement("Layers",
                from layer in _layers
                select layer.ToXml()
                );
            return layersXml;
        }

        bool hasSameName(ILayer layer)
        {
            bool hasSame = false;
            foreach (ILayer l in _layers)
            {
                if (l.Name == layer.Name)
                {
                    hasSame = true;
                    break;
                }
            }
            return hasSame;
        }

        bool CheckLayerBelongs(ILayer layer) 
        {
            if (_layers.Contains(layer)) {
                return true;
            } else {
                Logger.Warn("Layer '" + layer.Name + "' does not belongs to the layer manager.");
                return false;
            }
        }

        void SetZIndex()
        {
            foreach (ILayer layer in _layers)
            {
                int layerOrder = _layers.Count - _layers.IndexOf(layer);
                layer.ZIndex = layerOrder;
            }
        }
    }
}
